package org.imraniqbal.mouseland;

import java.util.Vector;

import org.imraniqbal.engine.Direction;
import org.imraniqbal.engine.Game;
import org.imraniqbal.engine.GameEvent;
import org.imraniqbal.engine.GameListener;
import org.imraniqbal.engine.Map;
import org.imraniqbal.engine.Mappable;
import org.imraniqbal.engine.Player;

/**
 * @author  imran
 */
public class Mouse extends Player implements GameListener {
	/**
	 * @uml.property  name="iD"
	 */
	private int ID;
	/**
	 * Creates a mouse with specified position and ID
	 * @param x X coordinate of the mouse
	 * @param y Y coordinate of the mouse
	 * @param iD Must be unique, Id of the ghost
	 * @author imran
	 */
	public Mouse(int x, int y, int iD) {
		super(x, y);
		this.setID(iD);
	}
	/**
	 * Creates a mouse with a specified ID, direction, postion and lives
	 * @param x X coordinate of the mouse
	 * @param y Y coordinate of the mouse
	 * @param direction Durection ( from Direction enum ) of mouse
	 * @param numOflives number of lives you wish mouse to have
	 * @param iD ID of the mouse, must be unique
	 * @author imran
	 */
	public Mouse(int x, int y, Direction direction, int numOflives, int iD) {
		super(x, y, direction, numOflives);
		this.setID(iD);
	}
	/**
	 * Spawn the mouse
	 * @author imran
	 */
	@Override
	public void spawn(Map map) {
		setX(7+getID());
		setY(5);
		map.addMappable(this);

	}
	/**
	 * Set ID to specified value
	 * @param iD  Id to be set
	 * @author  imran
	 * @uml.property  name="iD"
	 */
	public void setID(int iD) {
		ID = iD;
	}
	/**
	 * @return  ID of the mouse
	 * @author  imran
	 * @uml.property  name="iD"
	 */
	public int getID() {
		return ID;
	}
	/**
	 * Mouse will update itself accordingly due to event
	 * @author imran
	 */
	@Override
	public void onEvent(GameEvent e) {
		if(!(e.getSource() instanceof Game)){ return; }
		Game g = (Game) e.getSource();
		Vector<Mappable> m = g.getMap().getMappable(getX(), getY());
		if(m == null){
			return;
		}
		for(int i = 0; i < m.size(); i++){
			Mappable mpl = m.get(i);
			if( mpl instanceof Trap){
				this.die(g.getMap());
				((Trap)mpl).action(this, g.getMap());
			}
		}
			
	}
	/**
	 * @author imran
	 */
	@Override
	public boolean equals(Object obj) {
		if(obj instanceof Mouse){
			if(((Mouse)obj).getX() == this.getX() && ((Mouse)obj).getY() == this.getY()){
				return( ((Mouse)obj).getDirection() == this.getDirection() &&
						((Mouse)obj).getID() == this.getID());
			}
		}
		return false;
	}

	/**
	 * This function dictates the movement of the mice.
	 * The  mice will constantly try to chase you down, and their movement is decided on which path is the shortest distance
	 * @author Colin MacDougall
	 * @param X
	 * @param Y
	 * @param map
	 */
	public void moveMice(Hero hero, Map map){
		if(getNumOflives() == 0){ return; }
		//for the purpose of this method, 0 is up, 1 is right, 2 is down, 3 is left
		double uDist = 5;
		double rDist = 5;
		double dDist = 5;
		double lDist = 5;
		int numberofMice = 4;
		int randomfactor = 3; //this is a bit of a difficulty meter, set this higher to make mice more stupid
		int LargeDiscouragingNumber = 1000;
		int SlightlyLessLargeDiscouragingNumber = 990;
		//find the distance the mice is from hero for each possible move
		for (int i = 0; i < numberofMice; i++){
			switch(i){
			case 0://UP
				if ((map.isWall(this.getX(), this.getY()-1)) == false){
					//not a wall, you can move up
					uDist = Math.sqrt(Math.pow(Math.abs(hero.getY()-(this.getY()-1)),2) + Math.pow(Math.abs(hero.getX()-this.getX()),2));
				}else{//there is a wall
					uDist = LargeDiscouragingNumber; //set to large number so when normal, path won't be chosen
				}
				break;
			case 1://RIGHT
				if ((map.isWall(this.getX()+1, this.getY())) == false){
					//not a wall, you can move right
					rDist = Math.sqrt(Math.pow(Math.abs(hero.getY()-this.getY()),2) + Math.pow(Math.abs(hero.getX()-(this.getX()+1)),2));
				}else{//there is a wall
					rDist = LargeDiscouragingNumber; //set to large number so when normal, path won't be chosen
				}
				break;
			case 2://DOWN
				if ((map.isWall(this.getX(), this.getY()+1)) == false){
					//not a wall, you can move down
					dDist = Math.sqrt(Math.pow(Math.abs(hero.getY()-(this.getY()+1)),2) + Math.pow(Math.abs(hero.getX()-this.getX()),2));
				}else{//there is a wall
					dDist = LargeDiscouragingNumber; //set to large number so when normal, path won't be chosen
				}
				break;
			case 3://LEFT
				if ((map.isWall(this.getX()-1, this.getY())) == false){
					//not a wall, you can move left
					lDist = Math.sqrt(Math.pow(Math.abs(hero.getY()-this.getY()),2) + Math.pow(Math.abs(hero.getX()-(this.getX()-1)),2));
				}else{//there is a wall
					lDist = LargeDiscouragingNumber; //set to large number so when normal, path won't be chosen
				}
				break;
			}
		}
		// The following switch statement determines the direction you were going, and makes the opposite
		// direction less desirable, since mice should not spontaneously change direction
		// The desirability of a direction is based on the state of the hero, since we use large or small
		// numbers to make a direction more or less desirable based on whether hero is chasing them or
		// running away from them
		switch(this.getDirection()){
		case UP:
			dDist = SlightlyLessLargeDiscouragingNumber; 			 
			break;			 
		case RIGHT:
			lDist = SlightlyLessLargeDiscouragingNumber;
			break;
		case DOWN:
			uDist = SlightlyLessLargeDiscouragingNumber;
			break;
		case LEFT:
			rDist = SlightlyLessLargeDiscouragingNumber;
			break;
		}
		// Here, we're adding a small amount of randomness to the distances (a number between 0 and 1)
		// The small amount of randomness prevents situations where distances are exactly the same (hopefully)
		// but is a small enough change to not cause changes in what would be considered the right path to
		// take
		uDist += (Math.random()*randomfactor);
		rDist += (Math.random()*randomfactor);
		dDist += (Math.random()*randomfactor);
		lDist += (Math.random()*randomfactor);
		
		// By now, the opposite direction the ghost was travelling is less desirable than other paths, but
		// paths that would lead to walls are the least desirable
		if(uDist < rDist && uDist < dDist && uDist < lDist ){
			this.setDirection(Direction.UP);
		}else if(rDist < uDist && rDist < dDist && rDist < lDist){
			this.setDirection(Direction.RIGHT);
		}else if(dDist < uDist && dDist < rDist && dDist < lDist){
			this.setDirection(Direction.DOWN);
		}else if(lDist < uDist && lDist < rDist && lDist < dDist){
			this.setDirection(Direction.LEFT);
		}
		this.updateLocation(map); //actually move the mice
	}
}
